package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.AlibabaTTL;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 公共字段填充 切面
 */
@Slf4j
@Component
@Aspect
public class AutoFillAspect {

    //切入点                    mapper层所有方法 并且 添加了自定义的注解
    @Pointcut("@annotation(com.sky.annotation.AutoFill) && execution(* com.sky.*.mapper.*.*(..))")
    private void autoFillPointCut(){};


    //前置通知
    @Before("autoFillPointCut()")
    public void beforeAutoFillPointCut(JoinPoint jp){
        log.info(jp.getSignature().getDeclaringTypeName()+ "." + jp.getSignature().getName()+"()" + " -- 开始公共字段填充值.");

        // 1.获取当前被拦截的方法上 数据库操作类型 --> 枚举值
        // 签名对象强转为MethodSignature
        Signature signature = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;                 //方法签名对象
        AutoFill autoFill = methodSignature.getMethod().getAnnotation(AutoFill.class); //获得方法上的自定义注解对象
        OperationType operationType = autoFill.value();                                //注解中指定的数据库操作类型

        // 2.获取当前被拦截的方法参数--实体对象   < *做好约定,新增、修改都封装为实体对象，并且是方法第一个形参* >
        Object[] args = jp.getArgs();
        if(null == args || args.length == 0){
            return;
        }
        // 取出第一个参数 -- 实体
        Object entity = args[0];

        // 3.准备赋值数据
        LocalDateTime nowTime = LocalDateTime.now();
        Long userId = Long.valueOf(AlibabaTTL.threadLocalGet().toString());

        // 4.根据不同的操作类型，通过反射来为对应公共字段赋值
        if(operationType == OperationType.INSERT){
            // 插入操作，为4个公共字段赋值
            try {
                //反射获取方法对象
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                setCreateTime.setAccessible(true); //暴力反射
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                setUpdateTime.setAccessible(true); //暴力反射
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                setCreateUser.setAccessible(true); //暴力反射
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                setUpdateUser.setAccessible(true); //暴力反射

                //反射给公共字段赋值
                setCreateTime.invoke(entity, nowTime);
                setUpdateTime.invoke(entity, nowTime);
                setCreateUser.invoke(entity, userId);
                setUpdateUser.invoke(entity, userId);
            } catch (Exception e) {
                log.info(jp.getSignature().getDeclaringTypeName()+"."+jp.getSignature().getName()+"()"+" AOP为公共字段填充值异常");
            }
        } else if(operationType == OperationType.UPDATE){
            // 修改操作，为2个公共字段赋值
            try {
                //反射获取方法对象
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                setUpdateTime.setAccessible(true); //暴力反射
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                setUpdateUser.setAccessible(true); //暴力反射

                //反射给公共字段赋值
                setUpdateTime.invoke(entity, nowTime);
                setUpdateUser.invoke(entity, userId);
            } catch (Exception e) {
                log.info(jp.getSignature().getDeclaringTypeName()+"."+jp.getSignature().getName()+"()"+" AOP为公共字段填充值异常");
            }
        }
    }

}
